#include "container.hpp"
#include "ui.hpp"
#include <iostream>

using namespace Shiny;
using namespace Shiny::Widgets;
using namespace sf;

Container::Container()
{
  EventManager* em = EventManager::Get();

  if (em)
    em->MouseManager().AddListener(this);
  _typeCode          = type2code<Container>::code;
  _father            = 0;
  _backgroundColor   = sf::Color(255, 255, 255, 0);
  _hovered           = false;
  _clicked           = false;
  _isMovable         = false;
  _isMoving          = false;
  _isVisible         = true;
  _transitionIn      = false;
  _transitionOut     = false;
  _transition        = &Container::AnimationFade;
  _fade              = sf::Color(255.f, 255.f, 255.f, 255.f);
  _valign            = VNone;
  _halign            = None;
  _mouseAwareness    = MouseHover | MouseClick | MouseButton | MouseWheel2;
  _animationSpeed    = 1.f;
  _animationMoving   = false;
  _hasHoverGraphics  = false;
  _hasActiveGraphics = false;
}

Container::~Container()
{
    if (_father)
      _father->DelWidget(this);
    Register2EventManager(false);
    while (_children.size()) delete *(_children.begin());
}

Container* Container::operator[](const std::string& name)
{
  Children::iterator current = _children.begin();
  Children::iterator end     = _children.end();

  while (current != end)
  {
    if (*(*current) == name)
      return (*current);
    ++current;
  }
  return (0);
}

Container& Container::operator()(const std::string& name)
{
  Children::iterator current = _children.begin();
  Children::iterator end     = _children.end();

  while (current != end)
  {
    if (*(*current) == name)
      return (**current);
    ++current;
  }
  return (*this);
}

void Container::SetSize(const sf::Vector2f& size)
{
  Children::iterator current = _children.begin();
  Children::iterator end     = _children.end();

  this->Create(size.x, size.y);
  UpdateAlign();
  while (current != end)
    (*current++)->UpdateAlign();
}

void Container::AddWidget(Container* toAdd)
{
  _children.insert(_children.begin(), toAdd);
  toAdd->_father = this;
  toAdd->UpdateAlign();
  toAdd->Register2EventManager(false);
}

void Container::DelWidget(Container* toDel)
{
  _children.remove(toDel);
  toDel->_father = 0;
  toDel->Register2EventManager(true);
}

void Container::SelfFocus(void)
{
  if (_father)
    _father->TakeFocus(this);
  else
  {
    EventManager* em = EventManager::Get();
    
    if (em)
      em->MouseManager().SetFocus(this);
  }
}

void Container::TakeFocus(Container* takingFocus)
{
  _children.remove(takingFocus);
  _children.insert(_children.begin(), takingFocus);
  SelfFocus();
}

void Container::RenderSelf(void)
{
  this->Clear(_backgroundColor);
  if (_sprite.AnimationCount())
  {
    _sprite.Update();
    this->Draw(_sprite);
  }
}

void Container::Render(RenderTarget& target)
{
  Children::iterator current;

  if (!_isVisible && !_transitionOut)
    return ;
  RenderSelf();
  if (_children.begin() != _children.end())
  {
    current = _children.end();
    do
    {
      current--;
      (*current)->Render(*this);
    } while (current != _children.begin());
  }
  PostRender();
  if      (_transitionIn)    (this->*_transition)(_renderSprite, _transitionIn);
  else if (_transitionOut)   (this->*_transition)(_renderSprite, _transitionIn);
  if      (_animationMoving) UpdatePosition();
  this->Display();
  _renderSprite.SetTexture(GetTexture());
  _renderSprite.SetPosition(_position);
  target.Draw(_renderSprite);
  _refreshClock.Restart();
}

void Container::Move(const sf::Vector2f& position)
{
  _animationMovingPos    = position;
  _animationMovingStep.x = (_animationMovingPos.x - _position.x) / _animationSpeed;
  _animationMovingStep.y = (_animationMovingPos.y - _position.y) / _animationSpeed;
  _animationMoving       = true;
}

void Container::UpdatePosition(void)
{
  sf::Vector2f position = _position;
  sf::Vector2f toMove;

  toMove.x = _animationMovingStep.x * _refreshClock.GetElapsedTime().AsSeconds();
  toMove.y = _animationMovingStep.y * _refreshClock.GetElapsedTime().AsSeconds();

  if (_animationMovingPos.x != position.x)
    position.x += toMove.x;
  if (_animationMovingPos.y != position.y)
    position.y += toMove.y;

  if (((_position.x > _animationMovingPos.x && _animationMovingPos.x >= position.x) ||
       (_position.x < _animationMovingPos.x && _animationMovingPos.x <= position.x) ||
       (_position.x == _animationMovingPos.x)) &&
      ((_position.y > _animationMovingPos.y && _animationMovingPos.y >= position.y) ||
       (_position.y < _animationMovingPos.y && _animationMovingPos.y <= position.y) ||
       (_position.y == _animationMovingPos.y)))
  {
    position = _animationMovingPos;
    _animationMoving = false;
    MoveFinished.Emit(this);
  }
  SetPosition(position);
}

void Container::AnimationZoom(sf::Sprite& sprite, bool in)
{
  float value = (1.f / _animationSpeed) * _refreshClock.GetElapsedTime().AsSeconds();

  if (in && _scale.x == 1.f)
  {
    _scale.x = 0.f;
    _scale.y = 0.f;
  }
  else if (!in && _scale.x == 0.f)
  {
    _scale.x = 1.f;
    _scale.y = 1.f;
  }
  _scale.x += (in ? value : -value);
  _scale.y += (in ? value : -value);
  if (_scale.x <= 0.f)
  {
    _scale.x        = 0.f;
    _scale.y        = 0.f;
    _isVisible     = false;
    _transitionOut = false;
    AnimationFinished.Emit(this);
  }
  else if (_scale.x >= 1.f)
  {
    _scale.x        = 1.f;
    _scale.y        = 1.f;
    _isVisible     = true;
    _transitionIn  = false;
    AnimationFinished.Emit(this);
  }
  sprite.SetScale(_scale);
}

void Container::AnimationFade(sf::Sprite& sprite, bool in)
{
  float value = (255.f / _animationSpeed) * _refreshClock.GetElapsedTime().AsSeconds();
  float newValue;

  if      (in  && _fade.a == 255.f)
    _fade.a = 0.f;
  else if (!in && _fade.a == 0.f)
    _fade.a = 255.f;
  newValue = _fade.a + (_isVisible ? value : -value);
  if      (!in && (newValue <= 0.f || newValue > _fade.a))
  {
      newValue       = 0.f;
      _transitionOut = false;
      AnimationFinished.Emit(this);
  }
  else if (in  &&  (newValue >= 255.f || newValue < _fade.a))
  {
      newValue       = 255.f;
      _transitionIn  = false;
      AnimationFinished.Emit(this);
  }
  _fade.a = newValue;
  sprite.SetColor(_fade);
}

void Container::SetVisible(bool v)
{
  if (_isVisible != v)
  {
    if (_transition)
    {
      (v  ? _transitionIn : _transitionOut) = true;
      (!v ? _transitionIn : _transitionOut) = false;
    }
    _isVisible = v;
    if (!_father)
      Register2EventManager(_isVisible);
  }
}

void Container::Register2EventManager(bool reg)
{
  EventManager* em = EventManager::Get();

  if (em)
  {
    if (reg)
      em->MouseManager().AddListener(this);
    else
      em->MouseManager().DelListener(this);
  }
}

void Container::OtherEvents(const sf::Event)
{}

bool Container::mouseEvent(EventManager::Mouse::Event event)
{
  sf::IntRect  boundingBox;
  sf::Vector2f position = GetPosition();

  if (!_isVisible)
    return (false);
  boundingBox.Left   = position.x;
  boundingBox.Top    = position.y;
  boundingBox.Width  = GetWidth();
  boundingBox.Height = GetHeight();
  if (boundingBox.Contains(event.position.x, event.position.y))
  {
    Children::iterator current;
    Children::iterator end = _children.end();

    event.position.x -= position.x;
    event.position.y -= position.y;
    for (current = _children.begin() ; current != end ; ++current)
      if ((*current)->mouseEvent(event))
	return (true);
    switch (event.event.Type)
    {
      case sf::Event::MouseButtonPressed:
	return (EventMousePressed(event.event));
      case sf::Event::MouseButtonReleased:
	return (EventMouseRelease(event.event));
      case sf::Event::MouseWheelMoved:
	return (EventWheel(event.event));
      case sf::Event::MouseMoved:
	return (EventHovered(event.event));
    }
  }
  return (false);
}

void Container::SelectAnimation(void)
{
  if (_sprite.AnimationCount() == 0)
    return ;
  if (_hovered)
  {
    if (_clicked && _hasActiveGraphics)
      _sprite.SetAnimation(_hasHoverGraphics ? 2 : 1);
    else if (_hasHoverGraphics)
      _sprite.SetAnimation(1);
  }
  else
    _sprite.SetAnimation(0);
}

bool Container::EventHovered(const sf::Event event)
{
  if (!(_mouseAwareness & MouseHover))
    return (false);
  _hovered = true;
  SelectAnimation();
  EventManager::Get()->MouseManager().ExpectMouseMove(this);
  if (_isMoving)
  {
    sf::Vector2f newPos = GetPosition();
    
    newPos.x -= _mouseLastPos.x - event.MouseMove.X;
    newPos.y -= _mouseLastPos.y - event.MouseMove.Y;
    SetPosition(newPos);
    _mouseLastPos.x = event.MouseMove.X;
    _mouseLastPos.y = event.MouseMove.Y;
  }
  Hovered.Emit(this, event);
  return (true);
}

bool Container::EventUnhovered(const sf::Event event)
{
  if (!(_mouseAwareness & MouseHover))
    return (false);
  _hovered = false;
  SelectAnimation();
  Unhovered.Emit(this, event);
  return (true);
}

bool Container::EventClick(const sf::Event event)
{
  if (!(_mouseAwareness & MouseClick))
    return (false);
  Clicked.Emit(this, event);
  return (true);
}

bool Container::EventMousePressed(const sf::Event event)
{
  if (!(_mouseAwareness & MouseButton))
    return (false);
  SelfFocus();
  // Focus unknown events:
  {
    EventManager* em = EventManager::Get();

    em->WidgetsEventObserver.DisconnectAll();
    em->WidgetsEventObserver.Connect(*this, &Container::OtherEvents);
  }
  // End focus unknown events
  
  _clicked = true;
  SelectAnimation();
  EventManager::Get()->MouseManager().ExpectMouseRelease(this);
  if (_isMovable)
  {
    _isMoving       = true;
    _mouseLastPos.x = event.MouseButton.X;
    _mouseLastPos.y = event.MouseButton.Y;
  }
  MousePressed.Emit(this, event);
  return (true);
}

bool Container::EventMouseRelease(const sf::Event event)
{
  if (!(_mouseAwareness & MouseButton))
    return (false);
  if (_clicked)
    EventClick(event);
  _clicked  = false;
  SelectAnimation();
  _isMoving = false;
  MouseReleased.Emit(this, event);
  return (true);
}

bool Container::EventWheel(const sf::Event event)
{
  if (!(_mouseAwareness & MouseWheel2))
    return (false);
  MouseWheel.Emit(this, event);
  return (true);
}

void Container::ExpectedMouseRelease(void)
{
  _clicked = false;
  _isMoving = false;
  SelectAnimation();
}

void Container::ExpectedMouseMove()
{
  EventUnhovered(sf::Event());
}

void Container::SetAlign(AlignType align, VAlignType valign)
{
  _halign = align;
  _valign = valign;
  UpdateAlign();
}

void Container::SetPadding(float left, float top, float right, float bot)
{
  _paddingLeftTop.x  = left;
  _paddingLeftTop.y  = top;
  _paddingRightBot.x = right;
  _paddingRightBot.y = bot;
  UpdateAlign();
}

void Container::SetMargin(float left, float top, float right, float bot)
{
/*  if (_texture)
  {
//    _drawableZone.Width  = (_texture->GetWidth() - left) + right;
//    _drawableZone.Height = (_texture->GetHeight() - top) + bot;
    _drawableZone.Left   = left;
    _drawableZone.Top    = top;
  }*/
}

void Container::UpdateAlign(void)
{
  if (_father)
  {
    sf::Vector2f  fatherSize(_father->GetWidth(), _father->GetHeight());
    sf::Vector2f  selfSize(GetWidth(), GetHeight());
    sf::Vector2f  position(GetPosition());

    switch (_halign)
    {
      case Left:
        position.x = 0;
        break ;
      case Right:
        position.x = fatherSize.x - selfSize.x;
        break ;
      case Center:
        position.x = (fatherSize.x / 2) - (selfSize.x) / 2;
        break ;
      case None:
        break ;
    }
    switch (_valign)
    {
      case Top:
        position.y = 0;
        break ;
      case Bot:
        position.y = fatherSize.y - selfSize.y;
        break ;
      case VCenter:
        position.y = (fatherSize.y / 2) - (selfSize.y) / 2;
        break ;
      case VNone:
        break ;
    }
    position.x += _paddingLeftTop.x + _paddingRightBot.x;
    position.y += _paddingLeftTop.y + _paddingRightBot.y;
    SetPosition(position);
  }
}
